package org.example.ams.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 项目线程池配置
 * ThreadPoolTaskExecutor是jdk提供的ThreadPoolExecutor的再封装，
 * springboot默认为我们提供了一个ThreadPoolTaskExecutor的bean,我们
 * 可以使用注解@Async在方法上开启异步线程，或者直接注入bean使用多线程。
 * 但是在boot中要想使用ThreadPoolTaskExecutor，必须在配置类上使用@EnableAsync启用
 * 线程池。
 * @author
 */
@EnableAsync
@Configuration
public class ExecutorPoolConfig {

    @Bean
    @Primary
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 1. 设置核心线程池的大小
        /**
         * cpu的一个核心同时只能执行一个线程。
         * 对于IO密集型的系统，由于io会阻塞线程执行，所以核心空闲比较多，此时可以让cpu核心切换到其他线程上下文。
         * 因此io密集型核心线程数设置的多一点，但是又不能太多，否则会导致cpu核心频繁调度，性能低下。2倍比较合适，
         * 实际项目中可以以该值为基础进行调优。
         *
         * 反之对于cpu密集型系统，cpu核心比较繁忙，此时尽量减少线程切换，所以核心线程数设置的少一点，刚好等于cpu核心数
         * 就好，最后根据该基值进行调优。
         */
        // 获取cpu核心数
        int cpuCoreNum = Runtime.getRuntime().availableProcessors();
        threadPoolTaskExecutor.setCorePoolSize(cpuCoreNum);
        // 2. 设置最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(cpuCoreNum);
        // 3. 设置阻塞队列大小
        threadPoolTaskExecutor.setQueueCapacity(cpuCoreNum);
        // 4. 设置拒绝策略
        /**
         * 1. AbortPolicy 终止策略 默认 抛出RejectedExecutionException
         * 2. DiscardPolicy 抛弃策略，直接丢弃不做任何操作
         * 3. DiscardOldestPolicy 抛弃旧任务策略，将阻塞队列头的任务丢弃
         * 4. CallerRunsPolicy 调用者运行策略，直接调用runable的run方法运行
         * 对于重要的任务可以使用缓存机制解决，或者直接使用策略4，对于不重要的任务可以直接丢弃
         */
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 5. 设置线程无处理任务时，保持的活动时间
        threadPoolTaskExecutor.setKeepAliveSeconds(60);
        // 设置核心线程永不过时
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(false);
        // 6. 设置线程前缀名
        threadPoolTaskExecutor.setThreadNamePrefix("TaskExecutorThreadPool-");
        return  threadPoolTaskExecutor;
    }
}
